On Tue, 6 Feb 1996, Lincoln Stein wrote: > In general I think that it's much better to search for and accept only > the good patterns rather than trusting things to work when you exclude > the bad characters. People forget that the shell isn't the only > vulnerable program, and other programs may be subvertible by input > quite distinct from the set of shell metacharacters. > > Lincoln Ditto. I use a simply "check" routine to check every acceptable form variable. Here are the subs I regularly use to accomplish this. If you spot a bug, I'd appreciate a note. If you use my routines, please leave a ref to me somewhere. This example uses Lincoln's CGI.pm to grab form variables, then a few of my own routines to check info, etc. Although I use these routines all the time, I haven't tested the actual example script itself, beware: __BEGIN__ #!/usr/local/bin/perl ######## ## These routines are normally part of a package that I import: ######## $SENDMAIL = '/usr/lib/sendmail -t -n'; $DATE = localtime(time); ## To keep the perl jobs from taking priority over regular httpd daemons setpriority(0,0,4); #------------------------------------------------------------------------ sub log_it { my $time = localtime($^T); my $message = $_[0]; $message =~ tr/\n\"/ /; ($LOG = $_[1]) unless (defined $LOG); my $addr = $ENV{'REMOTE_ADDR'} || 'LOCAL'; my $host = $ENV{'REMOTE_HOST'} || 'LOCAL'; open(LOG, ">>$LOG") || die("Failed to open log file: $LOG\n"); printf LOG ("[%s] %s %s \"%s\"\n",$time,$host,$addr,$message); } #------------------------------------------------------------------------ sub check { my($pname,$OKstring,$OKpexp,$error) = @_; $OK{$pname} = $OKstring; printf ("%-15s\n%-15s %-20s\n","${pname}=${$pname}",$OKpexp,$OKstring) if (defined $DEBUG); (${$pname} =~ /^$OKpexp$/) || &error("\"${pname}\" (${$pname}) $ERR $OKstring\n"); return 1; } #------------------------------------------------------------------------ ## Beware of passing unchecked headers from the web!!! sub send_mail { my($to,$from,$subject,$message,@headers) = @_; open (MAIL, "|-") || exec($SENDMAIL); print MAIL <<EOM; Date: $DATE To: $to From: $from Subject: $subject @headers $message EOM close MAIL; } #------------------------------------------------------------------------ sub get_info { ## Take over form params for ($query->param){ (s/M_//) ? (@{$_} = $query->param("${&}$_")) : (${$_} = $query->param($_)); } } #------------------------------------------------------------------------ sub error { my $message = @_[0]; print <<EOM; Content-type: text/htm
$message
EOM (defined $LOG) && &log_it($message); die($message); } #------------------------------------------------------------------------ sub redirect { my $url = @_[0]; print <<EOM; Status: 302 Moved Temporarily Method: GET URI: <$url> Location: $url Content-type: text/htm EOM } #------------------------------------------------------------------------ sub validpw { my $salt = (getpwnam($_[0]))[1]; return (crypt($_[1], $salt) eq $salt) ? 1: 0 ; } ################################################################# ### End Subroutines ################################################################# ### MAIN (Here's where the untested stuff begins) use CGI; $query = new CGI; get_info; ### Just to check one paramter that might come a little close to a shell check('PARAM','\w{2,20}','2-20 alphanumerics allowed.'); ## To check for form parameters that aren't going to be passed to a shell, ## but are at least required: $REQ = '.{1,1000}'; $REQtxt = 'This field is required (up to 1000 characters).'; for ('PARAM2','PARAM2','PARAM3','PARAM4','PARAM5','PARAM6') { check($_,$REQ,$REQtxt); } print $query->header; print $query->start_htm; $PARAM2 $PARAM3 ## You get the idea ... EOM print $query->end_htm; __END__ Rob Muhlestein CGI Guy Teleport Internet Services http://www.teleport.com/